package pdfsetter.core.schema.advanced;

import java.util.Iterator;
import java.util.List;

import pdfsetter.core.schema.FillingSchema;
import pdfsetter.core.schema.PageSelection;
import pdfsetter.core.util.RandomAccessIterable;
import pdfsetter.core.util.RandomAccessIterableIterator;

/**
 * RepetitiveFileListProcessPattern is a bit different from {@link ConsecutiveFileListProcessPattern}. We also have list with files and attached selectors but
 * we take pages one by one from different files processing the list in repetitive way. So at the beginning we take first pages from each file, than second
 * pages, than third and so on. Of course on the list files number can be repeated. Important fact is that we continue taking pages until we use all pages from
 * selections. So when some file is bigger than others we have to take pages from outside the selection. In place of them we use blank pages.
 * 
 * @author Andrzej Dorobisz
 */
public class RepetitiveFileListProcessPattern implements FillingSchemaPattern {

    private final AttachedFilePageSelector[] attachedSelectors;
    private final int expectedFileNumber;

    public RepetitiveFileListProcessPattern(List<AttachedFilePageSelector> filesAndSelectors) {
        attachedSelectors = new AttachedFilePageSelector[filesAndSelectors.size()];
        // TODO exceptions, what if file number is less than zero
        int maxFileNumber = -1;
        int i = 0;
        for (AttachedFilePageSelector attachedSelector : filesAndSelectors) {
            if (attachedSelector.getFileNumber() > maxFileNumber)
                maxFileNumber = attachedSelector.getFileNumber();
            attachedSelectors[i++] = attachedSelector;
        }
        expectedFileNumber = maxFileNumber + 1;
    }

    @Override
    public FillingSchema produceFillingSchema(int[] fileSizes) {
        if (expectedFileNumber > fileSizes.length)
            throw new RuntimeException(); // TODO error!
        AttachedFilePageSelection[] attachedSelections = new AttachedFilePageSelection[attachedSelectors.length];
        for (int i = 0; i < attachedSelectors.length; i++) {
            attachedSelections[i] = attachedSelectors[i].produceAttachedSelection(fileSizes[attachedSelectors[i].getFileNumber()]);
        }
        return new RepetitiveFileListFillingSchema(attachedSelections);
    }

    @Override
    public int getExpectedNumberOfFiles() {
        return expectedFileNumber;
    }

    private class RepetitiveFileListFillingSchema implements FillingSchema {
        private final AttachedFilePageSelection[] pageSelections;
        private final int size;

        public RepetitiveFileListFillingSchema(AttachedFilePageSelection[] attachedSelections) {
            this.pageSelections = attachedSelections;
            int maxSize = 0;
            int maxPos = 0;
            for (int i = 0; i < pageSelections.length; i++) {
                int curSize = pageSelections[i].getFilePageSelection().size();
                if (curSize >= maxSize) {
                    maxSize = curSize;
                    maxPos = i;
                }
            }
            if (maxSize <= 0)
                size = 0;
            else {
                size = pageSelections.length * (maxSize - 1) + maxPos + 1;
            }
        }

        @Override
        public RandomAccessIterable<PageSelection> getPageSelections() {
            return new RandomAccessIterable<PageSelection>() {
                @Override
                public Iterator<PageSelection> iterator() {
                    return new RandomAccessIterableIterator<PageSelection>(this);
                }

                @Override
                public PageSelection get(int index) {
                    if (index < 0 || index >= size)
                        throw new IndexOutOfBoundsException();
                    try {
                        return pageSelections[index % pageSelections.length].getPageSelection(index / pageSelections.length);
                    } catch (IndexOutOfBoundsException e) {
                        return new PageSelection(-1, -1); // the meaning of this is BlankPage
                    }
                }

                @Override
                public int size() {
                    return size;
                }
            };
        }

        @Override
        public int getExpectedNumberOfFiles() {
            return expectedFileNumber;
        }
    }
}
